﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Babylon.Toolbox
{
    public sealed class Model
    {
        GraphicsDevice device;

        public ReadOnlyCollection<ModelMesh> Meshes { get; private set; }

        public ReadOnlyCollection<VertexBuffer> VertexBuffers { get; private set; }

        public ReadOnlyCollection<IndexBuffer> IndexBuffers { get; set; }

        public GraphicsDevice Device
        {
            get { return device;  }
            set { device = value; }
        }

        public Bone RootBone { get; set; }
        public ReadOnlyCollection<Bone> Bones { get; private set; }
        public ReadOnlyDictionary<string, Bone> IndexedBones { get; private set; }

        public BoundingSphere BoundingSphere { get;  set; }

        public Model(GraphicsDevice device, IList<VertexBuffer> vertexBuffers, IList<IndexBuffer> indexBuffers, IList<ModelMesh> meshes, IEnumerable<Bone> hierarchy = null)
        {
            this.device = device;
            IndexBuffers = new ReadOnlyCollection<IndexBuffer>(indexBuffers);
            VertexBuffers = new ReadOnlyCollection<VertexBuffer>(vertexBuffers);
            Meshes = new ReadOnlyCollection<ModelMesh>(meshes);

            foreach (var modelMesh in meshes)
            {
                modelMesh.Model = this;
            }

            if (hierarchy == null)
                RootBone = new Bone("root", Matrix.Identity, -1);
            else
            {
                List<Bone> bones = new List<Bone>();
                foreach (var bone in hierarchy)
                {
                    bones.Add(bone);
                    if (bone.ParentIndex >= bones.Count)
                        throw new InvalidOperationException(Strings.BonesMustBeSorted);
                }
                RootBone = bones[0];
                Bones = new ReadOnlyCollection<Bone>(bones);
                IndexedBones = new ReadOnlyDictionary<string, Bone>(
                    Bones.ToDictionary(b=>b.Name));
            }
        }

        public void Draw()
        {
            VertexBuffer previous = null;
            IndexBuffer priviousIb = null;

            foreach (ModelMesh mesh in Meshes)
            {
                if (mesh.VertexBuffer != previous)
                    device.SetVertexBuffer(mesh.VertexBuffer);

                if (mesh.IndexBuffer != priviousIb){
                    device.Indices = mesh.IndexBuffer;
                }

                mesh.Draw();

                previous = mesh.VertexBuffer;
                priviousIb = mesh.IndexBuffer;
            }
        }
        public void CopyBonesTo(Matrix[] bones){
            for (int i = 0; i < Bones.Count; i++){
                bones[i] = Bones[i].Transform;
            }
        }
        public void CopyAbsoluteBonesTo(Matrix[] absoluteBones)
        {
            // we assume bones are sorted
            for (int i = 0; i < Bones.Count; i++)
            {
                if (Bones[i].ParentIndex == -1)
                {
                    absoluteBones[i] = Bones[i].Transform;
                }
                else
                {
                    absoluteBones[i] = Bones[i].Transform * absoluteBones[Bones[i].ParentIndex];
                }
            }
        }
    }
}
